package cn.htsyb.synergy.tester.frame;

import cn.htsyb.synergy.tester.config.HttpConfig;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

public class HttpFrame {

    static Log log = LogFactory.getLog(HttpFrame.class);

    private static ConnectionConfig connConfig;

    private static SocketConfig socketConfig;

    private static ConnectionSocketFactory plainSF;

    private static KeyStore trustStore;

    private static SSLContext sslContext;

    private static LayeredConnectionSocketFactory sslSF;

    private static Registry<ConnectionSocketFactory> registry;

    private static PoolingHttpClientConnectionManager connManager;

    private static CloseableHttpClient clsClient;

    private static BasicCookieStore cookieStore;

    private static final String encoding = HttpConfig.getLocalCoding();

    private static final int maxTotal = HttpConfig.getMaxTotal();
    private static final int maxPerRoute = HttpConfig.getMaxPerRoute();
    private static final int idleTimeoutMs = HttpConfig.getIdleTimeoutMs();
    private static final int checkIdleTimeMs = HttpConfig.getCheckIdleTimeMs();
    private static final int socketTimeoutMs = HttpConfig.getSocketTimeoutMs();
    private static final String proxyIp = HttpConfig.getProxyIp();
    private static final Integer proxyPort = HttpConfig.getProxyPort();

    static {
        init();
    }

    protected HttpFrame() {

        init();
    }

    private static void init() {
        // 设置连接参数
        connConfig = ConnectionConfig.custom().setCharset(Charset.forName(encoding)).build();
        socketConfig = socketTimeoutMs > 0 ? SocketConfig.custom().setSoTimeout(socketTimeoutMs).build()
                : SocketConfig.custom().build();
        trustStrategyRegistry();
        connManager = registry != null ? new PoolingHttpClientConnectionManager(registry)
                : new PoolingHttpClientConnectionManager();
        connManager.setDefaultConnectionConfig(connConfig);
        connManager.setDefaultSocketConfig(socketConfig);
        // 指定cookie存储对象
        cookieStore = new BasicCookieStore();

        if (maxTotal > 0) {
            connManager.setMaxTotal(maxTotal);
        }
        connManager.setDefaultMaxPerRoute(maxPerRoute);
        connManager.closeIdleConnections(idleTimeoutMs, TimeUnit.MILLISECONDS);
        connManager.setValidateAfterInactivity(checkIdleTimeMs);
        HttpClientBuilder httpBuilder = HttpClientBuilder.create().setDefaultCookieStore(cookieStore)
                .setConnectionManager(connManager);
        if (StringUtils.isNotEmpty(proxyIp)) {
            httpBuilder.setProxy(new HttpHost(proxyIp, proxyPort));
        }
        clsClient = httpBuilder.build();
    }

    private static void trustStrategyRegistry() {
        RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
        plainSF = new PlainConnectionSocketFactory();
        registryBuilder.register("http", plainSF);
        // 指定信任密钥存储对象和连接套接字工厂
        try {
            trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            sslContext = SSLContextBuilder.create().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
                    .build();
            sslSF = new SSLConnectionSocketFactory(sslContext);
            registryBuilder.register("https", sslSF);
        } catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
            throw new SecurityException(e);
        }
        registry = registryBuilder.build();
    }

    /**
     * 将Response读取成字符串
     *
     * @param httpResp
     * @return
     * @throws IOException
     */
    public static String respToStr(HttpResponse httpResp) throws IOException {
        if (httpResp == null) {
            return null;
        }
        return EntityUtils.toString(httpResp.getEntity(), encoding);

    }

    /**
     * 构建httpclient
     *
     * @return HttpClientBuilder
     */
    public static HttpClientBuilder buildHttpClient() {

        return HttpClientBuilder.create().setConnectionManager(connManager);

    }

    /**
     * 执行HTTP请求
     *
     * @param httpRequestBase 构建的HTTP请求对象
     * @return CloseableHttpResponse 可关闭的HTTP返回对象
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static CloseableHttpResponse execRequest(HttpRequestBase httpRequestBase)
            throws ClientProtocolException, IOException {

        return clsClient.execute(httpRequestBase);
    }

    /**
     * 将字符串式的参数转换为NameValuePair
     *
     * @param param "key=value,key=value..."
     * @return
     * @throws UnsupportedEncodingException
     */
    public static NameValuePair[] hangNameValuePair(String param) throws UnsupportedEncodingException {
        if (StringUtils.isEmpty(param)) {
            return null;
        }
        String[] keyValues = param.split(",");
        if (ArrayUtils.isEmpty(keyValues)) {
            return null;
        }
        int num = keyValues.length;
        NameValuePair[] pairs = new NameValuePair[keyValues.length];
        for (int idx = 0; idx < num; idx++) {
            String keyValue = keyValues[idx];
            int index = keyValue.indexOf("=");
            if (index > 0) {
                pairs[idx] = new BasicNameValuePair(keyValue.substring(0, index),
                        URLDecoder.decode(keyValue.substring(index + 1), encoding));
            }
        }

        return pairs;

    }

    /**
     * 将字符串式的参数转换为Headers
     *
     * @param param "key=value,key=value..."
     * @return
     * @throws UnsupportedEncodingException
     */

    public static Header[] hangHeaders(String param) throws UnsupportedEncodingException {

        if (StringUtils.isEmpty(param)) {
            return null;
        }
        String[] keyValues = param.split(",");
        if (ArrayUtils.isEmpty(keyValues)) {
            return null;
        }
        int num = keyValues.length;
        Header[] pairs = new Header[keyValues.length];
        for (int idx = 0; idx < num; idx++) {
            String keyValue = keyValues[idx];
            int index = keyValue.indexOf("=");
            if (index > 0) {
                pairs[idx] = new BasicHeader(keyValue.substring(0, index),
                        URLDecoder.decode(keyValue.substring(index + 1), encoding));
            }
        }

        return pairs;

    }

    /**
     * 构建 post请求对象
     *
     * @param url
     * @param headers
     * @param params
     * @param entity
     * @return
     * @throws URISyntaxException
     * @throws UnsupportedEncodingException
     */
    public static HttpPost buildPost(String url, Header[] headers, NameValuePair[] params, HttpEntity entity)
            throws URISyntaxException, UnsupportedEncodingException {
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        HttpPost requert = new HttpPost();

        URIBuilder builder = new URIBuilder(url);
        if (ArrayUtils.isNotEmpty(params)) {
            builder.setParameters(params);
        }
        requert.setURI(builder.build());

        if (ArrayUtils.isNotEmpty(headers)) {
            requert.setHeaders(headers);
        }

        if (entity != null) {
            requert.setEntity(entity);
        }

        return requert;
    }

    /**
     * 简单get请求
     *
     * @param url
     * @param header
     * @param param
     * @return
     * @throws ClientProtocolException
     * @throws UnsupportedEncodingException
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String getAsStr(String url, String header, String param)
            throws ClientProtocolException, UnsupportedEncodingException, IOException, URISyntaxException {
        header = StringUtils.isEmpty(header) ? null : header;
        param = StringUtils.isEmpty(param) ? null : param;
        String resultStr = respToStr(execRequest(buildGet(url, hangHeaders(header), hangNameValuePair(param))));
        log.info("get [url:" + url + ",header:" + header + ",param:" + param + "] result: \n"
                + resultStr);
        return resultStr;

    }

    /**
     * 简单post请求
     *
     * @param url
     * @param header
     * @param param
     * @return
     * @throws ClientProtocolException
     * @throws UnsupportedEncodingException
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String postXmlAsStr(String url, String header, String param, String xmlStr)
            throws ClientProtocolException, UnsupportedEncodingException, IOException, URISyntaxException {
        header = StringUtils.isEmpty(header) ? null : header;
        param = StringUtils.isEmpty(param) ? null : param;
        xmlStr = StringUtils.isEmpty(xmlStr) ? null : xmlStr;
        String resultStr = respToStr(
                execRequest(buildPost(url, hangHeaders(header), hangNameValuePair(param), buildXmlEntity(xmlStr))));
        log.info("post [url:" + url + ",header:" + header + ",param:" + param + ",xmlStr:" + xmlStr + "] result: \n"
                + resultStr);
        return resultStr;
    }

    /**
     * 简单post请求
     *
     * @param url
     * @param header
     * @param param
     * @return
     * @throws ClientProtocolException
     * @throws UnsupportedEncodingException
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String postJsonAsStr(String url, String header, String param, String jsonStr)
            throws ClientProtocolException, UnsupportedEncodingException, IOException, URISyntaxException {
        header = StringUtils.isEmpty(header) ? null : header;
        param = StringUtils.isEmpty(param) ? null : param;
        jsonStr = StringUtils.isEmpty(jsonStr) ? null : jsonStr;
        String resultStr = respToStr(
                execRequest(buildPost(url, hangHeaders(header), hangNameValuePair(param), buildJsonEntity(jsonStr))));

        log.info("post [url:" + url + ",header:" + header + ",param:" + param + ",jsonStr:" + jsonStr + "] result: \n"
                + resultStr);
        return resultStr;

    }

    /**
     * 简单post文件上传请求
     *
     * @param url
     * @param header
     * @param param
     * @return
     * @throws ClientProtocolException
     * @throws UnsupportedEncodingException
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String postPairAsStr(String url, String header, String param, String file)
            throws ClientProtocolException, UnsupportedEncodingException, IOException, URISyntaxException {
        header = StringUtils.isEmpty(header) ? null : header;
        param = StringUtils.isEmpty(param) ? null : param;
        file = StringUtils.isEmpty(file) ? null : file;
        String resultStr = respToStr(execRequest(buildPost(url, hangHeaders(header), hangNameValuePair(param),
                buildMultipartEntity(hangNameValuePair(param), StringUtils.isEmpty(file) ? null : file.split(",")))));
        log.info("post [url:" + url + ",header:" + header + ",param:" + param + ",file:" + file + "] result: \n"
                + resultStr);
        return resultStr;
    }

    /**
     * 构建get请求对象
     *
     * @param url
     * @param headers
     * @param params
     * @return
     * @throws URISyntaxException
     * @throws UnsupportedEncodingException
     */
    public static HttpGet buildGet(String url, Header[] headers, NameValuePair[] params)
            throws URISyntaxException, UnsupportedEncodingException {
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        HttpGet requert = new HttpGet();

        URIBuilder builder = new URIBuilder(url);
        if (ArrayUtils.isNotEmpty(params)) {
            builder.setParameters(params);
        }
        requert.setURI(builder.build());

        if (ArrayUtils.isNotEmpty(headers)) {
            requert.setHeaders(headers);
        }

        return requert;
    }

    /**
     * 处理Resuful接口的参数替换
     *
     * @param resufulUrl 参考[http://127.0.0.1/api/{0}/{1}...]
     * @param values
     * @return
     */
    public static String handResufulUrl(String resufulUrl, String[] values) {
        String requertUrl = resufulUrl;
        if (StringUtils.isNotEmpty(resufulUrl) && ArrayUtils.isNotEmpty(values)) {
            requertUrl = MessageFormat.format(resufulUrl, values);
        }
        return requertUrl;
    }

    /**
     * 构建JSON数据的实体
     *
     * @param jsonBody
     * @return
     * @throws UnsupportedEncodingException
     */
    public static HttpEntity buildJsonEntity(String jsonBody) throws UnsupportedEncodingException {
        if (StringUtils.isNotEmpty(jsonBody)) {
            StringEntity strEntity = new StringEntity(jsonBody);
            if (StringUtils.isNotEmpty(encoding)) {
                strEntity.setContentEncoding(encoding);
            }
            strEntity.setContentType("application/json");
            return strEntity;
        }
        return null;

    }

    /**
     * 构建XMl数据的实体
     *
     * @param xmlbody
     * @return
     * @throws UnsupportedEncodingException
     */
    public static HttpEntity buildXmlEntity(String xmlbody) throws UnsupportedEncodingException {
        if (StringUtils.isNotEmpty(xmlbody)) {
            StringEntity strEntity = new StringEntity(xmlbody);
            if (StringUtils.isNotEmpty(encoding)) {
                strEntity.setContentEncoding(encoding);
            }
            strEntity.setContentType("application/xml");
            return strEntity;
        }
        return null;

    }

    /**
     * 构建form数据的实体
     *
     * @param formParams
     * @return
     * @throws UnsupportedEncodingException
     */
    public static HttpEntity handFormEntity(NameValuePair[] formParams) throws UnsupportedEncodingException {
        if (ArrayUtils.isNotEmpty(formParams)) {
            return new UrlEncodedFormEntity(Arrays.asList(formParams));
        }
        return null;

    }

    /**
     * 构建带文件上传form数据的实体
     *
     * @param formParams
     * @param localFiles
     * @return
     * @throws UnsupportedEncodingException
     */
    public static HttpEntity buildMultipartEntity(NameValuePair[] formParams, String[] localFiles)
            throws UnsupportedEncodingException {
        MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
        entityBuilder = entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

        if (ArrayUtils.isNotEmpty(localFiles)) {
            for (String localFile : localFiles) {

                File fileObj = new File(localFile, encoding);
                if (fileObj.exists() && !fileObj.isDirectory()) {
                    FileBody fileBody = new FileBody(fileObj);
                    entityBuilder.addPart(fileObj.getName(), fileBody);
                }
            }
        }
        if (ArrayUtils.isNotEmpty(formParams)) {
            for (NameValuePair nameValuePair : formParams) {
                entityBuilder.addTextBody(nameValuePair.getName(), nameValuePair.getValue());
            }
        }

        return entityBuilder.build();

    }

}
